/*
=======================================================================

  Bodyguard - BOTS
  
=======================================================================
*/
#include "g_local.h"

#include "bots_game2cgame.h"

#include "bg_local.h"
#include "bots_local.h"
#include "bots_bodyguard.h"

#define DECOY_DELAY		20000   // delay of 30 seconds
#define LASER_RANGE		1500	// BG must be closer to lasers than this value


void bots_updateClientData_bodyguard(gentity_t *self, bots_g2cg_bodyguard_t *p) {
	int tmp;


	if (!self->client)
		return;

	// Proxies? grenade/weapon modes?
	p->weapons.gren1		= self->client->i_numProx;
	p->weapons.gren2		= 0;
	p->weapons.grenademode	= self->client->qb_proxMode;
	p->weapons.weaponmode 	= 0;

	// Lasers
	BOTS_SETBIT(p->bits, self->client->qb_laserState,			BOTS_G2CG_BODYGUARD_LASERON);
	// Positive time when heating up (on) and negative for cooling (off)
	if (self->client->qb_laserState) {
		p->sLaserTime = 1-ceil((self->client->i_laserStartTime - level.time)/1000);
	}
	else 
		p->sLaserTime = -(floor((self->client->i_laserStopTime - level.time)/1000));

	// Decoy
	tmp = floor(((self->client->i_decoyStopTime+DECOY_DELAY) - level.time)/1000) + 1; // Geez
	p->sDecoyDelay = (tmp > 0)?tmp:0;
	
	// Protection
	BOTS_SETBIT(p->bits, self->client->protect,					BOTS_G2CG_BODYGUARD_PROTECTING);
	// Hmm... that one needs to be latched, or the client is gonna miss state changes
	// I don't like to do that, but i'm going to reuse a slot (iWarcryDebounce)
	tmp = (self->team_protect && self->team_protect != self)?self->team_protect->s.clientNum:-1;
	if (tmp == -1) {
		if ((p->sProtected != -1) && (self->client->iWarcryDebounce < level.time)) {
			// Latch
			p->sProtected = -1;
		}
	}
	else {
		p->sProtected = tmp;
		self->client->iWarcryDebounce = level.time + 3000; // 3s debounce
	}


	// Pulse
	BOTS_SETBIT(p->bits, self->client->qb_laserPulseMode,		BOTS_G2CG_BODYGUARD_PULSE);
}

/////////////////////////////////////////////
//
// BOTS_GetPulseTime
//
// This function will return the time when the next pulse will occur.
// Pass in true if it is called fromt he client think routine, else false.
// The time is calculated by multiplying the distance from the farthest laser
// by 10 and adding the level.time to it.
//
int BOTS_GetPulseTime(gentity_t* ent)
{
	float	len1 = 0;
	float	len2 = 0;
	vec3_t	tempVec;
	float	pulse = level.time;

	if (!ent->client)
		return level.time;

	if (!ent->laserbeam)
		return level.time;

	// check the distance
	VectorSubtract(ent->s.pos.trBase, ent->laserbeam->s.pos.trBase, tempVec);
	len1 = VectorLength(tempVec);

	if (ent->laserbeam2)
	{
		VectorSubtract(ent->s.pos.trBase, ent->laserbeam2->s.pos.trBase, tempVec);
		len2 = VectorLength(tempVec);
	}

	// take the greater of the 2
	if (len1 < len2)
		len1 = len2;

	if (ent->client->pers.classlevel > 0)
		pulse = len1 * 4 / ent->client->pers.classlevel;

	return (pulse + level.time);
}

/////////////////////////////////////////////
//
// BOTS_laserbase_explode
//
// This will give off an explosion effect.  I would normally just call
// G_ExplodeMissile, but that doesn't take into account the wall position.
// G_ExplodeMissile just explodes in the direciton 0,0,1 - not normal to the wall
// the explosion took place on.
//
void BOTS_laserbase_explode( gentity_t* self )
 {
	G_AddEvent( self, EV_MISSILE_MISS, DirToByte( self->movedir ) );
	self->freeAfterEvent = qtrue;

	G_RadiusDamage (self->s.pos.trBase, self, LASER_MOUNT_DAMAGE, LASER_MOUNT_DAMAGE_RADIUS, NULL, MOD_TARGET_LASER);

	trap_LinkEntity( self );
}

/////////////////////////////////////////////
//
// BOTS_laser_pulse_think - helper routine for BOTS_laser_think
//
// This will determine if the laser needs to be turned off or on and
// for how long.
//
// RETURNS
//    qfalse : laser is off
//	  qtrue	 : laser is on
//
qboolean BOTS_laser_pulse_think (gentity_t* self) 
{
	gentity_t* ent;
	int modifier = 1;

	// safety check - impossible though
	if (self->parent)
		ent = self->parent;
	else
		return qtrue;

	// level 2 or greater bg's get longer laser times
	if (ent->client->pers.classlevel > 2)
		modifier = 2;

	// pulse mode is off
	if (!ent->client->qb_laserPulseMode)
		return qtrue;

	if (ent->client->i_laserPulseTime <= level.time)
	{
		// toggle the state
		ent->client->qb_laserState = !ent->client->qb_laserState;

		// set the state timer
		ent->client->i_laserPulseTime = BOTS_GetPulseTime(ent);
		
		if (ent->client->qb_laserState)
			ent->client->i_laserMaxTime = level.time + LASER_TIME * modifier;
		else
			trap_UnlinkEntity(self);
	}

	// no idea why, but we have to do this...
	if (self == ent->laserbeam2)
	{
		if (!ent->client->qb_laserState)
			trap_UnlinkEntity(self);
	}

	return ent->client->qb_laserState;
}

/////////////////////////////////////////////
//
// BOTS_laser_think - laser think routine
//
// This is the brains behind the laser.
// If the current time has exceeded the time allowed for
// a laser, the think function will destroy the laserbase and
// free the laser.
//
#define MAX_LASER_HITS 10
void BOTS_laser_think (gentity_t* self) 
{
	vec3_t		end;
	trace_t		tr;
	gentity_t*	ent;
	gentity_t*	unlinkedEntities[MAX_LASER_HITS];
	int			unlinked = 0;
	int			i;

	if (self->parent)
		ent = self->parent;
	else
		return;

	if (!self->parent->client)
		return;

	// has our laser been on too long?
	if (level.time > ent->client->i_laserMaxTime)
	{
		// gentity_t* tent;

		if (self == ent->laserbeam)
		{
			ent->laserbase->nextthink = level.time;
			ent->laserbeam = NULL;
		}
		if (self == ent->laserbeam2)
		{
			ent->laserbase2->nextthink = level.time;
			ent->laserbeam2 = NULL;
		}

		// do some damage

		// make an explosion - not working!!
		// tent = G_TempEntity( self->s.pos.trBase, EV_MISSILE_MISS );
		// tent->s.eventParm = DirToByte( self->movedir );

		G_FreeEntity(self);

		ent->client->qb_laserState = qfalse;
		return;
	}

	// check to see if we will trace a beam and display it this think - pulse mode
	if (!BOTS_laser_pulse_think(self))
	{
		self->nextthink = level.time + FRAMETIME;
		return;
	}
	
	// fire forward and see what we hit
	VectorMA (self->s.pos.trBase, 8192, self->movedir, end);

	do 
	{
		gentity_t* traceEnt;

		trap_Trace (&tr, self->s.pos.trBase, NULL, NULL, end, self->s.number, MASK_SHOT );

		if ( tr.entityNum >= ENTITYNUM_MAX_NORMAL )
			break;
	
		traceEnt = &g_entities[ tr.entityNum ];

		// hurt it if we can
		if ( traceEnt->takedamage && ent != traceEnt)
			if (!OnSameTeam (ent, traceEnt))
				G_Damage (traceEnt, self, self->activator, self->movedir, tr.endpos, self->damage, 
						  DAMAGE_NO_KNOCKBACK, MOD_TARGET_LASER);

		// we hit something solid enough to stop the beam
		if ( tr.contents & CONTENTS_SOLID ) 
			break;		
		
		// unlink this entity, so the next trace will go past it
		trap_UnlinkEntity( traceEnt );
		unlinkedEntities[unlinked] = traceEnt;
		unlinked++;
	} 
	while ( unlinked < MAX_LASER_HITS );

	// link back in any entities we unlinked
	for ( i = 0 ; i < unlinked ; i++ )
		trap_LinkEntity( unlinkedEntities[i] );
	
	// save net bandwidth
	SnapVector( tr.endpos );

	// set endpoint of laser
	VectorCopy (self->s.pos.trBase, self->s.origin);
	VectorCopy (tr.endpos, self->s.origin2);
	trap_LinkEntity( self );

	// do the same for the reflection of the beam off of the surface
	// I could probably put this in it's own function... but I didn't.. :)
	// If we want to do this more than once I will move it.
	// Only do this for the 2 standard lasers, do not reflect the reflections.
	// Only level 4 bodyguards get this.
	// don't reflect off of the sky
	if (!(tr.surfaceFlags & SURF_SKY) && (ent->client->pers.classlevel >= 3) && (self == ent->laserbeam || self == ent->laserbeam2))
	{
		gentity_t*	tent;
		trace_t		tr2;
		vec3_t		forward;
		unlinked	= 0;


		// create a temporary entity for this bounced beam
		tent				= G_TempEntity( tr.endpos, ET_BOTS_LASER );
		tent->s.eType		= ET_BOTS_LASER;	// redundant
		tent->s.modelindex	= 1;
		tent->s.modelindex2	= 0; // This is non-zero if it's a dropped item
		tent->s.number		= tent - g_entities;
		tent->s.frame		= 2;	// beam diameter
		tent->classname		= "bots_laser";
		tent->damage		= LASER_DAMAGE;
		tent->activator		= ent;
		tent->parent		= ent;
		tent->s.pos.trTime	= level.time;
		tent->nextthink		= level.time + FRAMETIME;
		tent->think			= BOTS_laser_think;
		tent->s.clientNum	= ent->s.clientNum;

		// setup laser movedir (projection of laser)
		VectorCopy(tr.plane.normal, tent->movedir);

		if (VectorLength(self->user_angle))
			VectorCopy(self->user_angle, tent->movedir);

		// fire forward and see what we hit
		VectorMA (tr.endpos, 8192, tent->movedir, forward);

		do 
		{
			gentity_t* traceEnt;

			trap_Trace (&tr2, tr.endpos, NULL, NULL, forward, tent->s.number, MASK_SHOT );

			if ( tr2.entityNum >= ENTITYNUM_MAX_NORMAL )
				break;
		
			traceEnt = &g_entities[ tr2.entityNum ];

			// hurt it if we can
			if ( traceEnt->takedamage && ent != traceEnt)
				if (!OnSameTeam (ent, traceEnt))
					G_Damage (traceEnt, self, self->activator, tr2.plane.normal, tr2.endpos, self->damage, 
							  DAMAGE_NO_KNOCKBACK, MOD_TARGET_LASER);

			// we hit something solid enough to stop the beam
			if ( tr2.contents & CONTENTS_SOLID ) 
				break;		
			
			// unlink this entity, so the next trace will go past it
			trap_UnlinkEntity( traceEnt );
			unlinkedEntities[unlinked] = traceEnt;
			unlinked++;
		} 
		while ( unlinked < MAX_LASER_HITS );
		
		// link back in any entities we unlinked
		for ( i = 0 ; i < unlinked ; i++ )
			trap_LinkEntity( unlinkedEntities[i] );

		// save net bandwidth
		SnapVector( tr.endpos );

		// set endpoint of laser
		VectorCopy (tr.endpos, tent->s.origin);
		VectorCopy (tr2.endpos, tent->s.origin2);
	}// reflection beam

	self->nextthink = level.time + FRAMETIME;
}

/////////////////////////////////////////////
//
// BOTS_laseron - turn a laser on
//
// Call to the laser's think routine.  The think routine will link
// the entity with the world and set it's nextthink time.
//
void BOTS_laseron (gentity_t* self)
{
	if (!self->activator)
		self->activator = self;

	BOTS_laser_think (self);
}

/////////////////////////////////////////////
//
// BOTS_laseroff - turn a laser off
//
// The entitiy is still there, we just unlink it from the world and
// not have it call to it's think routine.
//
void BOTS_laseroff (gentity_t* self)
{
	trap_UnlinkEntity( self );
	self->nextthink = 0;
}

/////////////////////////////////////////////
//
// BOTS_laser_setup - figures out whether to call BOTS_laseroon or BOTS_laseroff
//
// This function will determine whether it needs to call BOTS_laseron or
// BOTS_laseroff.  It will also setup the needed delay timers.
//
// ai_mode = 0 - toggle
//           1 - on
//           2 - off
//
void BOTS_laser_setup (int clientNum, int ai_mode)
{
	int modifier = 1;
	gentity_t* ent = g_entities + clientNum;

	qboolean	laser1OK = qfalse;
	qboolean	laser2OK = qfalse;

	// no lasers
	if (!ent->laserbeam && !ent->laserbeam2)
	{
		BOTS_cp_console( clientNum, "You have to place a laser first.");
		return;
	}

	// level 2 or greater bg's get longer laser times
	if (ent->client->pers.classlevel > 2)
		modifier = 2;

	if (ai_mode == 1 && ent->client->qb_laserState)
		return;
	
	if (ai_mode == 2 && !ent->client->qb_laserState)
		return;
	
	if (!ent->client->qb_laserState)
	{
		float	len1 = 0;
		float	len2 = 0;
		vec3_t	tempVec;
		char	laserStateString[MAX_STRING_CHARS];
		laserStateString[0] = 0;

		// laser must cool off
		if (level.time < ent->client->i_laserCoolDown)
		{
			BOTS_cp_console( clientNum, "Your laser must cool off first.");
			return;
		}

		if (ent->laserbeam)
		{	
			VectorSubtract(ent->s.pos.trBase, ent->laserbeam->s.pos.trBase, tempVec);
			len1 = VectorLength(tempVec);

			if (len1 < LASER_RANGE)
			{
				ent->client->i_laserStartTime	= level.time;
				ent->client->i_laserMaxTime		= level.time + LASER_TIME * modifier;
				ent->client->qb_laserState		= qtrue;

				BOTS_laseron(ent->laserbeam);
				laser1OK = qtrue;
			}
		}

		if (ent->laserbeam2)
		{
			VectorSubtract(ent->s.pos.trBase, ent->laserbeam2->s.pos.trBase, tempVec);
			len2 = VectorLength(tempVec);

			if (len2 < LASER_RANGE)
			{
				if (!ent->client->qb_laserState)
				{
					ent->client->i_laserStartTime	= level.time;
					ent->client->i_laserMaxTime		= level.time + LASER_TIME * modifier;
					ent->client->qb_laserState		= qtrue;
				}

				BOTS_laseron(ent->laserbeam2);
				laser2OK = qtrue;
			}
		
		}
		else
			laser2OK = qtrue;

		if (!laser1OK)
			Com_sprintf( laserStateString, sizeof( laserStateString ), 
						 "Laser 1 is out of range.\n");
		if (!laser2OK)
			Com_sprintf( laserStateString, sizeof( laserStateString ), 
						 "%sLaser 2 is out of range.\n", laserStateString);

		if (laserStateString[0])
			BOTS_cp_console( clientNum, laserStateString);
	}
	else
	{
		ent->client->qb_laserState = qfalse;

		if (ent->laserbeam)
			BOTS_laseroff(ent->laserbeam);

		if (ent->laserbeam2)
			BOTS_laseroff(ent->laserbeam2);

		if (ent->client->i_laserStartTime)
		{
			if (level.time - ent->client->i_laserStartTime < 2000)
				ent->client->i_laserCoolDown = level.time + 2000;
			else
			ent->client->i_laserCoolDown = level.time + (level.time - ent->client->i_laserStartTime);
			ent->client->i_laserStopTime = ent->client->i_laserCoolDown; // used for the countdown in the hud
		}
	}
}


/////////////////////////////////////////////
//
// BOTS_laserkill - destroy all lasers and laser mounts
//
// To destroy a laser we just set the delay to the level time minus the
// max amount of time possible for the laser.  The think function will 
// destroy a laser if it has been on too long.
//
void BOTS_laserkill(int clientNum, qboolean bMute)
{
	gentity_t* ent = g_entities + clientNum;

	if (!ent->client)
		return;

	if (!ent->laserbeam && !ent->laserbeam2 && !bMute)
		BOTS_cp_console( clientNum, "You have to place a laser first.");

	// set the time less than the current so the think function destroys it
	ent->client->i_laserMaxTime = level.time - 1;

	if (ent->laserbeam)
	{
		BOTS_laseron(ent->laserbeam);
		ent->laserbeam = NULL;
	}

	if (ent->laserbeam2)
	{
		BOTS_laseron(ent->laserbeam2);
		ent->laserbeam2 = NULL;
	}
	
	// get laser values ready for next set - might not be necessary
	ent->client->i_laserMaxTime		= 0;
	ent->client->i_laserStartTime	= 0;
	ent->client->i_laserCoolDown	= 0;
	ent->client->qb_laserState		= qfalse;
}

/////////////////////////////////////////////
//
// BOTS_laser - places a laser
//
// This will put a laser and a laser mount entity into the world.
//
void BOTS_laser (int clientNum)
{
	vec3_t		wallp;
	vec3_t		forward, right, up;
	vec3_t		muzzle;
	trace_t		tr;
	qboolean	qb_secondlaser = qfalse;

	gentity_t*	ent = g_entities + clientNum;
	gentity_t*	laser;
	gentity_t*	laser_mount;

	const int iBOTS_BODYGUARD_FREEZE_DELAY = 2500;


	// check to see if this class can set a laser
	if (ent->bots_class != BOTS_BODYGUARD)
	{
		BOTS_cp_console( clientNum, "Only the Bodyguard can place a laser");
		return;
	}

	// check ammo
	if (ent->client->ps.ammo[WP_BFG] < 50)
	{
		BOTS_cp_console( clientNum, "You need at least 50 cells for a laser.");
		return;
	}

	if (ent->client->pers.classlevel <= 1 && ent->laserbeam)
	{
		BOTS_cp_console( clientNum, "You have already built a laser.");
		return;
	}

	if (ent->client->pers.classlevel > 1)
	{
		if (!ent->laserbeam)
			qb_secondlaser = qfalse;
		else if (!ent->laserbeam2)
			qb_secondlaser = qtrue;
		else
		{
			BOTS_cp_console( clientNum, "You have already built 2 lasers.");
			return;
		}		
	}

	// figure out what laser we want to make
/*	if (ent->laserbeam)
	{
		if (ent->client->pers.classlevel > 1)
		{
			if (ent->laserbeam2)
			{
				BOTS_cp_console( clientNum, "You have already built 2 lasers.");
				return;
			}
			else
				qb_secondlaser = qtrue;
		}
		else
		{
			BOTS_cp_console( clientNum, "You have already built a laser.");
			return;
		}
	}
*/
	// set aiming directions
	AngleVectors (ent->client->ps.viewangles, forward, right, up);

	// setup muzzle point
	CalcMuzzlePoint(ent, forward, NULL, NULL, muzzle);

	VectorNormalize (forward);

	// Setup end point of the vector we want to trace
	VectorCopy(muzzle, wallp);
	VectorMA(wallp, 50, forward, wallp);

	// do the trace
	trap_Trace(&tr, muzzle, NULL, NULL, wallp, ent->s.number, MASK_SOLID);

	// Line complete ? (ie. no collision)
	if (tr.fraction == 1.0 ||
	    (Q_stricmp (g_entities[tr.entityNum].classname, "func_door") == 0))
	{
		BOTS_cp_console( clientNum, "Too far from wall.");
		return;
	}

	// wall found
	BOTS_cp_console( clientNum, "Laser attached.");

	// cell usage
	ent->client->ps.ammo[WP_BFG] -= 50;

	// save net bandwidth
	SnapVector( tr.endpos );

	//
	// Make the laser beam
	//
	laser					= G_Spawn();
	laser->s.eType			= ET_BOTS_LASER; // ET_BEAM ET_BOTS_LASER
	laser->s.modelindex		= 1;
	laser->s.modelindex2	= 0; // This is non-zero if it's a dropped item
	laser->s.number			= laser - g_entities;
	laser->s.frame			= 2;	// beam diameter
	laser->classname		= "bots_laser";
	laser->damage			= LASER_DAMAGE;
	laser->activator		= ent;
	laser->parent			= ent;
	laser->s.pos.trTime		= level.time;
	laser->nextthink		= 0;
	laser->think			= BOTS_laser_think;
	laser->s.clientNum		= ent->s.clientNum;
	VectorClear(laser->user_angle);

	// Set orgin of laser to the end of the lasermount model (aprox 10u)
	VectorMA(tr.endpos, 10, tr.plane.normal, wallp);
	G_SetOrigin(laser, wallp);

	// setup laser movedir (projection of laser)
	VectorCopy(tr.plane.normal, laser->movedir);
	VectorCopy(tr.plane.normal, laser->s.angles);

	trap_LinkEntity (laser);

	// assign the entity to the player
	if (qb_secondlaser)
		ent->laserbeam2 = laser;
	else
	{
		ent->laserbeam	= laser;
		ent->client->i_laserCoolDown	= 0;
		ent->client->i_laserStartTime	= 0;
	}

	//
	// Attatch a model for the laser mount
	//
	laser_mount					= G_Spawn();
	laser_mount->s.eType		= ET_GENERAL;
	laser_mount->s.modelindex	= //G_ModelIndex("models/ammo/grenade1.md3");
		G_ModelIndex("models/bots/laser_pod.md3");
	laser_mount->s.modelindex2	= 0; // This is non-zero if it's a dropped item
	laser_mount->s.number		= laser_mount - g_entities;
	laser_mount->classname		= "laser_mount";
	laser_mount->r.contents		= CONTENTS_SOLID;
	laser_mount->clipmask		= CONTENTS_SOLID;
	laser_mount->parent			= laser;

	// set the movedir so the explosion looks right
	VectorCopy(tr.plane.normal, laser_mount->movedir);
	vectoangles(tr.plane.normal, laser_mount->s.angles);

	// give it a rocket explosion
	laser_mount->think			= BOTS_laserbase_explode;
	laser_mount->s.weapon		= WP_ROCKET_LAUNCHER;
	laser->s.pos.trType			= TR_LINEAR;

	G_SetOrigin(laser_mount, tr.endpos);

	// rotate the mount
	vectoangles(tr.plane.normal, laser_mount->s.apos.trBase);
	//VectorCopy( up, laser_mount->s.apos.trBase );

	trap_LinkEntity (laser_mount);

	// assign the entity to the player
	if (qb_secondlaser)
		ent->laserbase2 = laser_mount;
	else
		ent->laserbase = laser_mount;

	//tbp: freeze him for a moment
	ent->client->iLaserSetupFreezeTime = level.time + iBOTS_BODYGUARD_FREEZE_DELAY;
}

/////////////////////////////////////////////
//
// BOTS_protect - toggles protect mode
//
// This will toggle protect mode for the bodyguard.
// If it was previously on, it will clear all necessar protect fields.
//
void BOTS_protect(int clientNum)
{
	gentity_t*	ent = g_entities + clientNum;

	// check to see if this class can set a laser
	if (ent->bots_class != BOTS_BODYGUARD)
	{
		BOTS_cp_console(clientNum, "Only the Bodyguard can use protect.");
		return;
	}

	if(ent->client->protect)
	{
		ent->client->protect = qfalse;

		// clear protector and protected fields
		if (ent->team_protect)
		{
			ent->team_protect->bg_protector = NULL;
			ent->team_protect = NULL;
		}
		
		/*
		BOTS_cp_console(clientNum, "Protect mode disabled.");
		*/
	}
	else
	{
		ent->client->protect = qtrue;
		/*
		BOTS_cp_console(clientNum, "Protect mode enabled.");
		*/
	}
}

/////////////////////////////////////////////
//
// BOTS_pulse - toggles pulse mode
//
// This will toggle pulse mode for the bodyguard.
//
void BOTS_pulse(int clientNum)
{
	gentity_t*	ent = g_entities + clientNum;

	// check to see if this class can use pulse mode
	if (ent->bots_class != BOTS_BODYGUARD)
	{
		BOTS_cp_console(clientNum, "Only the Bodyguard can use pulse.");
		return;
	}

	if (ent->client->pers.classlevel < 4)
	{
		BOTS_cp_console(clientNum, "You must be level 4 to use pulse lasers.");
		return;
	}

	if(ent->client->qb_laserPulseMode)
	{
		ent->client->qb_laserPulseMode = qfalse;
		/*
		BOTS_cp_console(clientNum, "Pulse mode disabled.");
		*/

		// turn off lasers
		if (ent->client->qb_laserState)
			BOTS_laser_setup(clientNum, 0);
	}
	else
	{
		ent->client->qb_laserPulseMode = qtrue;
		ent->client->i_laserPulseTime = BOTS_GetPulseTime(ent);
		/*
		BOTS_cp_console(clientNum, "Pulse mode enabled.");
		*/
	}
}

/////////////////////////////////////////////
//
// BOTS_decoy - turns on decoymode
//
// This will enable the bodyguard to look like he is carrying
// the other teams flag.
//
void BOTS_decoy(int clientNum)
{
	gentity_t*	ent = g_entities + clientNum;

	if (!ent->client)
		return;

	if (ent->client->keyindex > 0)
		return;

	// check to see if this class can use pulse mode
	if (ent->bots_class != BOTS_BODYGUARD)
	{
		BOTS_cp_console(clientNum, "Only the Bodyguard can use decoy.");
		return;
	}

	if (ent->client->pers.classlevel < 1)
	{
		BOTS_cp_console(clientNum, "You must be at least level 1 to be a decoy.");
		return;
	}

	if (ent->client->qb_decoyMode)
	{
		BOTS_cp_console(clientNum, "Decoymode already enabled.");
		return;
	}

	if (ent->client->i_decoyStopTime + DECOY_DELAY > level.time && level.time > 20000)
	{
		/*
		BOTS_cp_console(clientNum, va("You must wait %i seconds.", 
									   (ent->client->i_decoyStopTime + DECOY_DELAY - level.time)/1000));
		*/
		return;
	}

	ent->client->qb_decoyMode = qtrue;
	ent->client->i_decoyStopTime = level.time + 10000 * ent->client->pers.classlevel;

	if (ent->bots_team == TEAM_BLUE)
		ent->client->ps.powerups[PW_REDFLAG]	= ent->client->i_decoyStopTime;
	else
		ent->client->ps.powerups[PW_BLUEFLAG]	= ent->client->i_decoyStopTime;
}

void BOTS_laser_angle(int clientNum)
{
	gentity_t*	ent = g_entities + clientNum;
	vec3_t forward;

	if (!ent->client)
		return;

	if (ent->client->pers.classlevel < 3)
	{
		BOTS_cp_console(clientNum, "You must be at least level 3 to angle a laser.");
		return;
	}

	// set aiming directions
	AngleVectors (ent->client->ps.viewangles, forward, NULL, NULL);

	if (ent->laserbeam)
	{
		qboolean angleSet = qfalse;
		if (VectorLength(ent->laserbeam->user_angle) == 0)
		{
			VectorCopy(forward, ent->laserbeam->user_angle);
			angleSet = qtrue;
		}
		else if (ent->laserbeam2)
		{
			if (VectorLength(ent->laserbeam2->user_angle) == 0)
			{
				VectorCopy(forward, ent->laserbeam2->user_angle);
				angleSet = qtrue;
			}
		}
		
		if (!angleSet)
			BOTS_cp_console(clientNum, "Angle is already set.");
	}
	else
			BOTS_cp_console(clientNum, "You have no lasers to angle.");

}

/////////////////////////////////////////////
//
// BOTS_BodyguardCommands - Commands for the Bodyguard
//
// This will parse the passed cmd string and call to the correct functions.
// 
// RETURNS
//    qfalse : cmd is not a bodyguard command
//	  qtrue	 : cmd is a bodyguard command
//
qboolean BOTS_BodyguardCommands(int clientNum, char* cmd)
{
	if (Q_stricmp (cmd, "laser") == 0 )
	{
		BOTS_laser(clientNum);
		return qtrue;
	}
	else if (Q_stricmp (cmd, "lasertog") == 0 )
	{
		BOTS_laser_setup(clientNum, 0);
		return qtrue;
	}
	else if (Q_stricmp (cmd, "laseron") == 0 )
	{
		BOTS_laser_setup(clientNum, 1);
		return qtrue;
	}
	else if (Q_stricmp (cmd, "laseroff") == 0 )
	{
		BOTS_laser_setup(clientNum, 2);
		return qtrue;
	}
	else if (Q_stricmp (cmd, "laserkill") == 0 )
	{
		BOTS_laserkill(clientNum,qfalse);
		return qtrue;
	}
	else if (Q_stricmp (cmd, "protect") == 0 )
	{
		BOTS_protect(clientNum);
		return qtrue;
	}
	else if (Q_stricmp (cmd, "pulse") == 0 )
	{
		BOTS_pulse(clientNum);
		return qtrue;
	}
	else if (Q_stricmp (cmd, "decoy") == 0)
	{
		BOTS_decoy(clientNum);
		return qtrue;
	}
	else if (Q_stricmp (cmd, "angle") == 0)
	{
		BOTS_laser_angle(clientNum);
		return qtrue;
	}

	return qfalse;
}

/////////////////////////////////////////////
//
// BOTS_ClientThink_bodyguard
//
// Bodyguard think routine.  Called once for each client think if
// the client is a bodyguard
//
void BOTS_BG_CleanProtect(gentity_t *self) {
	gentity_t	*ent = g_entities;
	int			i;


	self->bg_protector = NULL;
	self->team_protect = NULL;
	for (i = 0; i < level.maxclients; ent++,i++)
		if (ent->bg_protector == self)
			ent->bg_protector = NULL;

}

void BOTS_BG_SetProtect(gentity_t *self) {
	int			e, numListedEntities, entityList[MAX_GENTITIES];
	vec3_t		mins, maxs;
	gentity_t	*ent;
	const float fPROTECT_RADIUS = 500.0f;

	for ( e = 0 ; e < 3 ; e++ ) {
		mins[e] = self->r.currentOrigin[e] - fPROTECT_RADIUS;
		maxs[e] = self->r.currentOrigin[e] + fPROTECT_RADIUS;
	}

	numListedEntities = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES );
	for ( e = 0 ; e < numListedEntities ; e++ ) {
		ent = &g_entities[entityList[ e ]];

		if (!ent->client)
			continue;
		if (ent->bots_class == BOTS_BODYGUARD)
			continue;
		if (ent->bg_protector)
			continue;
		if (!OnSameTeam(ent,self))
			continue;
		if (ent == self)
			continue;
		
		ent->bg_protector	= self;
		self->bg_protector	= self; // Ugly, prevents unneeded cleanup
	}
}

void BOTS_ClientThink_bodyguard(gentity_t* ent)
{
	// dead - clean it up
	if (ent->health <= 0)
		return;


	if (ent->bg_protector) // Only clean if needed
		BOTS_BG_CleanProtect(ent);
	if (ent->client->protect)
		BOTS_BG_SetProtect(ent);

	if (ent->client->ps.powerups[PW_REDFLAG] <= level.time &&
		ent->client->ps.powerups[PW_BLUEFLAG] <= level.time &&
		ent->client->qb_decoyMode)
	{
		ent->client->qb_decoyMode = qfalse;
	}

	// check to see if the person we are protecting is close enough
	/*
	if (ent->team_protect)
	{
		vec3_t distance;
		VectorSubtract(ent->s.pos.trBase, ent->team_protect->s.pos.trBase, distance);
		if (VectorLength(distance) > 1000)
		{
			ent->team_protect->bg_protector = NULL;
			ent->team_protect = NULL;

			// write client data to the config string
			BOTS_BodyguardConfigString(ent->s.clientNum);
		}
	}
	*/

	// Make sure the bg is close enough to use pulse.
	// If the bg is too far, turn off the lasers.
	if (ent->client->qb_laserPulseMode && ent->client->qb_laserState)
	{
		vec3_t	dist1;
		vec3_t	dist2;
		float	len1 = 0;
		float	len2 = 0;

		if (ent->laserbeam)
		{
			VectorSubtract(ent->s.pos.trBase, ent->laserbeam->s.pos.trBase, dist1);
			len1 = VectorLength(dist1);
		}

		if (ent->laserbeam2)
		{
			VectorSubtract(ent->s.pos.trBase, ent->laserbeam2->s.pos.trBase, dist2);
			len2 = VectorLength(dist2);
		}

		if (len1 < len2)
			len1 = len2;

		if (len1 > 1500)
			BOTS_laser_setup(ent->s.clientNum, 0);
	}
}
